package main

//tcpserver接口实现类，用户处理通信协议

import (
	"com/mixmedia/utils/SyncMap"
	"fmt"
	"runtime"
	"strings"
	"sync"
	"tcpsocket"
	"time"
)

const (
	END              = "{end}\x00"
	SPT              = "|"
	NewClientConnect = "S"
	GetRoomData      = "L"
	NewClientApply   = "J"
	LoginSucceed     = "C"
	PointerMove      = "M"
	PPTControl       = "P"
	VideoCmd         = "V"
	ClientExit       = "O"
	CCLogin          = "I"
	UpdateClientInfo = "U"
	DefaultTimeout   = 10 * time.Second
)

type room struct {
	clientMaps     *SyncMap.SS
	clientNameMaps *SyncMap.SS
	owner          string
	cleaning       bool
	lock           *sync.Mutex
}

func (r *room) inroom(clientName string, connkey string) {
	r.lock.Lock()
	defer r.lock.Unlock()
	r.clientMaps.Set(clientName, connkey)
	r.clientNameMaps.Set(connkey, clientName)
}

func (r *room) getClientList() (list []string) {
	list = make([]string, 0)
	for clientName, _ := range r.clientMaps.Items() {
		list = append(list, clientName, clientName)
	}
	return
}

func NewRoom(CCID string) (r *room) {
	r = &room{
		owner:    CCID,
		cleaning: false,
		lock:     new(sync.Mutex),
	}

	r.clientMaps = SyncMap.NewSS()
	r.clientNameMaps = SyncMap.NewSS()

	return
}

type EtHandler struct {
	server   tcpsocket.TcpServer
	roomMaps map[string]*room
	ccMaps   map[string]string
	buffer   map[string]string
	dbModel  *model
	Debug    bool
	lock     *sync.Mutex
}

func (h *EtHandler) RegisterServer(server tcpsocket.TcpServer) {
	h.server = server
	h.roomMaps = make(map[string]*room)
	h.ccMaps = make(map[string]string)
	h.buffer = make(map[string]string)
	h.lock = new(sync.Mutex)
	h.server.SetDebug(h.Debug)
}

func (h *EtHandler) HandleConnect(connkey string) {
	log.Info("%s connect 2 server", connkey)
}

func (h *EtHandler) HandleReade(connkey string, content string) {
	//fmt.Printf("get date:%q form %s \n", content, connkey)
	//handle flash socket policy
	if strings.Contains(content, "<policy-file-request/>") {
		log.Info("get policy request from:<%s>", connkey)
		h.server.Write(connkey, `<cross-domain-policy><allow-access-from domain="*" to-ports="*"/></cross-domain-policy>`+"\x00")
		return
	}
	h.routingCMD(content, connkey)
}

func (h *EtHandler) HandleWrite(connkey string, content string) (result string) {
	//	fmt.Printf("send to %s, data: %s \n", connkey, content)
	result = content
	return
}

func (h *EtHandler) HandleDisConnect(connkey string) {
	h.lock.Lock()
	defer h.lock.Unlock()
	log.Info("%s disconnect", connkey)
	etroom, status := h.roomMaps[connkey]
	if status && !etroom.cleaning {
		ccConnKey, err := h.ccMaps[etroom.owner]
		if err {
			//if it's CC
			if strings.EqualFold(ccConnKey, connkey) {
				etroom.cleaning = true
				for k, clientConnkey := range etroom.clientMaps.Items() {
					delete(h.roomMaps, clientConnkey)
					delete(h.buffer, clientConnkey)
					etroom.clientNameMaps.Delete(connkey)
					etroom.clientMaps.Delete(k)
					go h.server.Close(clientConnkey)
				}
				delete(h.roomMaps, connkey)
				delete(h.buffer, connkey)
				delete(h.ccMaps, etroom.owner)

				runtime.GC()
				return
			}
			clientName, err := etroom.clientNameMaps.Get(connkey)
			if err {
				etroom.clientMaps.Delete(clientName)
				etroom.clientNameMaps.Delete(clientName)
				delete(h.roomMaps, connkey)
				delete(h.buffer, connkey)
				cmds := []string{GetRoomData}
				cmds = append(cmds, etroom.getClientList()...)
				h.sendCommand(ccConnKey, cmds...)
			}
		}
	}
}

func (h *EtHandler) HandleError(connkey string, err error) {
	log.Info("Error: when handle %s get error: %s", connkey, err)
}

//send message to socket
func (h *EtHandler) sendCommand(connkey string, arg ...string) {
	args := append(arg, END)
	h.server.Write(connkey, strings.Join(args, SPT))
}

//routing cmd to handle func
func (h *EtHandler) routingCMD(content string, connkey string) {
	buffer, status := h.buffer[connkey]
	if !status {
		buffer = ""
	}
	src := buffer + content
	cmds := strings.Split(src, END)
	if cmds == nil {
		return
	}
	last := len(cmds) - 1
	for _, param := range cmds {
		params := strings.Split(param, SPT)

		params_count := len(params)
		if params == nil || params_count <= 0 {
			break
		}

		switch params[0] {
		case CCLogin:
			if params_count > 2 {
				h.handleCClogin(connkey, params[1], params[2])
			}
			break
		case NewClientApply:
			if params_count > 2 {
				h.handleNewClientApply(connkey, params[1], params[2])
			}
			break
		case NewClientConnect:
			if params_count > 2 {
				h.handleNewClientConnect(connkey, params[1], params[2])
			}
			break
		case UpdateClientInfo:
			if params_count > 3 {
				h.handleUpdateClientInfo(connkey, params[1], params[2], params[3])
			}
			break
		default:
			h.handleOtherCmd(connkey, params...)
			break
		}
	}
	h.buffer[connkey] = cmds[last]
}

func (h *EtHandler) handleCClogin(connkey string, username string, password string) {
	status := h.dbModel.VerifyCC(username, password)
	if !status {
		h.sendCommand(connkey, LoginSucceed, "NO")
		return
	}
	oCCconnkey, status := h.ccMaps[username]
	if status {
		h.server.Close(oCCconnkey)
	}
	h.ccMaps[username] = connkey
	h.sendCommand(connkey, LoginSucceed, "OK")
}

func (h *EtHandler) handleNewClientApply(connkey string, CCID string, clientName string) {
	ccConnkey, status := h.ccMaps[CCID]
	if !status {
		h.sendCommand(connkey, LoginSucceed, "NO")
		return
	}
	inroom, err := h.roomMaps[ccConnkey]
	if !err {
		inroom = NewRoom(CCID)
	}
	var clientConnKey string
	clientConnKey, status = inroom.clientMaps.Get(clientName)
	if status && !strings.EqualFold(clientConnKey, connkey) {
		clientName = fmt.Sprintf("%s-%d", clientName, len(inroom.clientMaps.Items()))
	}
	inroom.inroom(clientName, connkey)
	log.Info("client in room:%s <%s>", clientName, connkey)
	h.roomMaps[ccConnkey] = inroom
	h.roomMaps[connkey] = inroom
	cmds := []string{GetRoomData}
	cmds = append(cmds, inroom.getClientList()...)
	h.sendCommand(ccConnkey, cmds...)
}

func (h *EtHandler) handleNewClientConnect(connkey string, CCID string, clientName string) {
	etroom, status := h.roomMaps[connkey]
	if !status {
		return
	}
	clientConnkey, success := etroom.clientMaps.Get(clientName)
	if success {
		h.sendCommand(clientConnkey, LoginSucceed, "OK")
	}
}

func (h *EtHandler) handleUpdateClientInfo(connkey string, clientName string, OPNumber string, city string) {
	ccRoom, status := h.roomMaps[connkey]
	if status {
		log.Info("here will sync the ccinfo 2 database. CCID:%s, clientName:%s, OPNumber:%s, clientCity:%s \n", ccRoom.owner, clientName, OPNumber, city)
	}
}

func (h *EtHandler) handleOtherCmd(connkey string, args ...string) {
	ccRoom, status := h.roomMaps[connkey]
	if !status {
		return
	}
	ccConnkey, _ := h.ccMaps[ccRoom.owner]
	//if it's CC
	if strings.EqualFold(ccConnkey, connkey) {
		clientParams := make([]string, 0)
		switch args[0] {
		case PointerMove:
			if len(args) >= 3 {
				args[1] = PointerMove
				clientParams = args[1:]
			}
			break
		case VideoCmd:
			if len(args) > 1 {
				clientParams = args
			}
			break
		case PPTControl:
			if len(args) >= 3 {
				args[1] = PPTControl
				clientParams = args[1:]
			}
			break
		default:
			return
		}
		for _, clientConnkey := range ccRoom.clientMaps.Items() {
			h.sendCommand(clientConnkey, clientParams...)
		}
	} else {
		switch args[0] {
		case LoginSucceed:
			if len(args) >= 3 {
				clientParams := []string{LoginSucceed, ccRoom.owner, "unknow"}
				clientParams = append(clientParams, args[2:]...)
				h.sendCommand(ccConnkey, clientParams...)
			}
			break
		}
	}
}
